home *** CD-ROM | disk | FTP | other *** search
/ NeXTSTEP 3.1 (Developer) [x86] / NeXT Step 3.1 Intel dev.cdr.dmg / NextDeveloper / Examples / DatabaseKit / PubsDemo / Controller.m < prev    next >
Encoding:
Text File  |  1993-02-18  |  10.3 KB  |  380 lines

  1. /* Controller.m:
  2.  * You may freely copy, distribute, and reuse the code in this example.
  3.  * NeXT disclaims any warranty of any kind, expressed or  implied, as to its
  4.  * fitness for any particular use.
  5.  *
  6.  * Written by Mai Nguyen, NeXT Developer Support
  7.  *
  8.  */
  9.  
  10. #import <appkit/appkit.h>     
  11. #import    <dbkit/dbkit.h>
  12. #import <libc.h>
  13. #import "Controller.h"
  14.  
  15. /* Define localized strings */
  16. #define INSTALL_MODEL NXLocalizedString("Please install SybaseDemo.dbmodela into your project directory and restart.", NULL, "Notify user that SybaseDemo.dbmodela must be installed in his project directory.")
  17. #define EMPTY_STRING NXLocalizedString("Cannot accept empty string", NULL, "Notify user that empty string input is not valid.")
  18. #define INSERT_FAILED NXLocalizedString("Insert operation failed", NULL, "Notify user that insert operation has failed.")
  19.  
  20. static char newAuthorID[100];
  21.  
  22. @implementation Controller
  23.  
  24. /* Extract the actual database and recordlist from the DBModule UI Object
  25.  */ 
  26. -appDidInit:sender
  27. {
  28.         /* Notify the user if the database can't be found */
  29.     if (![DBDatabase findDatabaseNamed:"SybaseDemo" connect:YES]) {
  30.         NXRunAlertPanel(NULL,INSTALL_MODEL, "OK", NULL, NULL);
  31.         return self;
  32.     }    
  33.       dbDatabase = [dbModule database];
  34.     dbFetchGroup = [dbModule rootFetchGroup];
  35.     dbRecordList = [dbFetchGroup recordList];
  36.     recordOrder = DB_AscendingOrder;
  37.     dbQualifier = nil;
  38.     
  39.             /* Initialize the retrieval order to be ascending order
  40.              */
  41.     sortProp = [[dbModule entity] propertyNamed:"lastName"];
  42.             
  43.     [dbRecordList addRetrieveOrder:recordOrder for:sortProp];
  44.         
  45.             /* Assign the controller object to be the delegate of dbFetchGroup.
  46.              * See also the method fetchGroup:didInsertRecordAt:
  47.              */
  48.     [dbFetchGroup setDelegate:self];
  49.     
  50.             /* Assign the record list of the root fetchgroup to be the delegate
  51.              * of DBBinder. This is a convenient way to make sure that the proper
  52.              * qualifier will be used at fetch time.
  53.              * See also the method binderWillSelect:
  54.              */
  55.     [[dbFetchGroup recordList] setBinderDelegate:self];
  56.     
  57.             /* Create a DBValue instance to be used later for accessing data
  58.              * inside a record list.
  59.              */
  60.     aValue = [[DBValue alloc] init];
  61.     
  62.     [theWindow makeKeyAndOrderFront:nil];
  63.     return self;
  64. }
  65.  
  66. /* Change the retrieval order for the record list. To be set before pressing
  67.  * SELECT.
  68.  */
  69. -changeRetrieveOrder:sender
  70. {
  71.     recordOrder = [sender selectedTag];
  72.     return self;
  73. }
  74.  
  75. /* Build the DBQualifier before a SELECT operation. Note that you should give
  76.  * the external names as defined in dbModel for properties.
  77.  */
  78. - buildSelectQualifier:sender
  79. {
  80.     const char * state;
  81.     id stateProperty;
  82.     
  83.     state = [qualifierField stringValue];
  84.     if (!strcmp(state,"")) {
  85.         dbQualifier = nil;
  86.         return nil;
  87.     }
  88.     else {
  89.     stateProperty = [[dbModule entity] propertyNamed:"state"];
  90.     dbQualifier = [[DBQualifier allocFromZone:[self zone] ] 
  91.         initForEntity:[dbModule entity]
  92.         fromDescription:"%@ LIKE %s", stateProperty, state];
  93.     }
  94.     return self;
  95. }
  96.  
  97. /* This method is called when the SELECT button is pressed.
  98.  * NOTE: If the qualifier form has a NULL string, the source is set to nil,
  99.  * which means that ALL records will be fetched. 
  100.  */
  101. - select:sender
  102. {
  103.     id    source;
  104.  
  105.     [self buildSelectQualifier:nil];    
  106.     if (!dbQualifier)
  107.         source = nil;
  108.     else
  109.         source = [dbModule entity];
  110.         
  111.     [dbFetchGroup fetchContentsOf:source usingQualifier:dbQualifier];
  112.     
  113.         /* Update the display */
  114.     [self display];
  115.     return self;
  116. }
  117.  
  118. /* This method is called when the INSERT button is pressed. Check for blank
  119.  * record fields before the actual INSERT.
  120.  */
  121. - insert:sender
  122. {
  123.     if ([self checkInputRecord:sender]==nil)
  124.         NXRunAlertPanel(NULL,INSERT_FAILED, "OK", NULL, NULL);
  125.     else
  126.         [dbModule insertNewRecord:sender];
  127.     [self display];
  128.     return self;
  129. }
  130.     
  131. /* Try to highlight the newly added row based on the token "newAuthorID".
  132.  * This method is called everytime the SELECT button is pressed.
  133.  * If there is no match, the first row is highlighted by default.
  134.  */
  135. - display
  136. {
  137.     int rowCount, i, row;
  138.     id socSecProperty;
  139.     const char *keyValue;
  140.     
  141.     row = 0;
  142.         /* Search for a match only if the ID has meaningful data */
  143.     if ( strlen(newAuthorID) > 0 ) {
  144.         rowCount = [dbTableView rowCount];
  145.         socSecProperty = [[dbModule entity] propertyNamed:"authorID"];
  146.         for (i= 0; i < rowCount; i++) {
  147.             [dbRecordList getValue:aValue forProperty:socSecProperty at:i];
  148.             keyValue = (const char *)[aValue stringValue];
  149.             if (!strcmp(keyValue, newAuthorID)) {
  150.                 row = i;
  151.                 break; }
  152.         }
  153.     }
  154.     [dbFetchGroup setCurrentRecord:row];
  155.     [dbTableView display];
  156.     return self;
  157. }
  158.  
  159.  
  160. /* evaluateSQL
  161.  * This method uses a DBBinder object which will evaluate SQL queries entered
  162.  * by the user.
  163.  * Note that the results returned from the evaluation are PROPERTIES only.
  164.  * For example, if you enter: "select * from authors", all properties of the
  165.  * authors table will be returned. However, if you enter: "select au_lname from
  166.  * authors where state = 'CA'", only the last name property will be returned.
  167.  * You can reuse this code and substitute the query string with a Sybase stored
  168.  * procedure.
  169.  * Another possibility to evaluate a Sybase stored procedure would be to become
  170.  * the binder's delegate, and use the delegate methods binderWillSelect: or 
  171.  * binderDidSelect: to call the binder method evaluateString in order to launch
  172.  * the stored procedure.
  173.  */
  174. - evaluateSQL:sender
  175. {
  176.      int i,c, pc;
  177.     const char * queryString;
  178.     char buf[256];
  179.       id bind = [[DBBinder alloc] init];
  180.      id list = [[List alloc] init];
  181.      id propertyList = [[List alloc] init];
  182.       id prop = nil;
  183.       
  184.         /* set up the binder */
  185.        [bind setDatabase:dbDatabase];
  186.       [bind setContainer:list];
  187.       
  188.       if (queryString = [queryField stringValue]) {
  189.         [self appendToView:textView text:"SQL>>"];
  190.         [bind reset];
  191.         [bind evaluateString:queryString];
  192.         [bind fetch];
  193.         [bind getProperties:propertyList];
  194.         sprintf(buf, "Number of records found:  %d\n", (c=[list count]));
  195.         [self appendToView:textView text:buf];
  196.         sprintf(buf, "Number of properties in each record: %d\n", 
  197.                                                 (pc = [propertyList count]));
  198.         while (--c >= 0) {
  199.               [bind setTo:c];
  200.             sprintf(buf, "Record %d\n", c);
  201.             [self appendToView:textView text:buf];
  202.             [self appendToView:textView text:"------------"];
  203.  
  204.               for (i = 0; i < pc; i++) {
  205.                 prop = [propertyList objectAt:i];
  206.                 sprintf(buf, "%s: %s\t", [prop name],
  207.                 [[bind valueForProperty:prop] stringValue]);
  208.                 [self appendToView:textView text:buf];
  209.                   }
  210.             [self appendToView:textView text:"\n"];
  211.         }
  212.      }
  213.  
  214.     return self;
  215. }
  216.  
  217.  
  218. - appendToView:(id) scrollView text:(const char *)newText
  219. {
  220.     int currentLength = [[scrollView docView] textLength];
  221.     [[scrollView docView] setSel:currentLength :currentLength];
  222.     [[scrollView docView] replaceSel:newText];
  223.     [[scrollView docView] scrollSelToVisible];
  224.     return self;
  225. }
  226.  
  227. /* DBFetchGroup delegate methods */
  228.  
  229. /*
  230.  * This method is called at each SELECT operation.
  231.  */
  232. - fetchGroupWillFetch:fetchGroup
  233. {
  234.     [dbRecordList addRetrieveOrder:recordOrder for:sortProp];
  235.     return self;
  236. }
  237.  
  238. /*
  239.  * This method is called at each INSERT operation.
  240.  */
  241.  
  242. - fetchGroup:fetchGroup didInsertRecordAt:(int)index
  243. {
  244.         /* Fill in the record fields before inserting the new record */
  245.     [self fillNewRecordAt:index];
  246.     return self;
  247. }
  248.  
  249.  
  250. /* 
  251.  * This delegate method is called at each SAVE operation.
  252.  */
  253. - fetchGroupDidSave:fetchGroup
  254. {
  255.     [self clearData];
  256.     return self;
  257. }
  258.  
  259. /* 
  260.  * This binder delegate method is called at each SELECT operation so that 
  261.  * the proper qualifier is used.
  262.  */
  263.  
  264. - (BOOL)binderWillSelect:aBinder
  265. {
  266.   [aBinder setQualifier:dbQualifier];
  267.   return YES;
  268. }
  269.  
  270. /* checkInputRecord
  271.  * Check if any input field is empty.
  272.  */
  273. - checkInputRecord:sender
  274. {
  275.     int    i;
  276.     const char *inputStr;
  277.         
  278.     for ( i = 0; i < 9; i++ ) {
  279.          inputStr = (const char *)[formMatrix stringValueAt:i];
  280.             /* If the string is empty, abort the operation */
  281.         if  ( inputStr == NULL){
  282.             NXRunAlertPanel (NULL,EMPTY_STRING,NULL, NULL, NULL);
  283.             return nil;
  284.             }
  285.     }
  286.          return self;
  287. }
  288.  
  289. /* fillNewRecordAt:(int) index
  290.  * Method used for INSERT operation 
  291.  * Get the user input from the form cells. 
  292.  */
  293. - fillNewRecordAt:(int)index
  294. {
  295.     const char *inputString;
  296.     int    contractNum;
  297.         
  298.             /* set last name */        
  299.     inputString = (const char *)[formMatrix stringValueAt:0];
  300.     [aValue setStringValue:inputString];
  301.     [dbRecordList setValue:aValue 
  302.                 forProperty:[[dbModule entity] propertyNamed:"lastName"]
  303.                 at:index];    
  304.                 
  305.         /* set first name */
  306.     inputString = (const char *)[formMatrix stringValueAt:1];
  307.     [aValue setStringValue:(const char *)inputString];
  308.     [dbRecordList setValue: aValue
  309.                 forProperty: [[dbModule entity] propertyNamed:"firstName"]
  310.                 at:index];
  311.                 
  312.         /* get the ssn or author id */
  313.      inputString = (const char *)[formMatrix stringValueAt:2];
  314.     [aValue setStringValue:inputString];
  315.     [dbRecordList setValue:aValue 
  316.                 forProperty:[[dbModule entity] propertyNamed:"authorID"]
  317.                 at:index];
  318.     strcpy( newAuthorID, inputString);
  319.             
  320.             /* set address */
  321.     inputString = (const char *)[formMatrix stringValueAt:3];
  322.     [aValue setStringValue: inputString];
  323.     [dbRecordList setValue: aValue
  324.                 forProperty:[[dbModule entity] propertyNamed:"address"]
  325.                 at:index];
  326.  
  327.             /* set city name */
  328.     inputString = (const char *)[formMatrix stringValueAt:4];
  329.     [aValue setStringValue: inputString];
  330.     [dbRecordList setValue: aValue
  331.             forProperty:[[dbModule entity] propertyNamed:"city"]
  332.             at:index];
  333.  
  334.             /* set state */
  335.     inputString = (const char *)[formMatrix stringValueAt:5];
  336.     [aValue setStringValue: inputString];
  337.     [dbRecordList setValue: aValue
  338.             forProperty:[[dbModule entity] propertyNamed:"state"]
  339.             at:index];
  340.  
  341.             /* set zip code */
  342.     inputString = (const char *)[formMatrix stringValueAt:6];
  343.     [aValue setStringValue: inputString];
  344.     [dbRecordList setValue: aValue
  345.             forProperty:[[dbModule entity] propertyNamed:"zipCode"]
  346.             at:index ];
  347.     
  348.             /* set phone number */
  349.     inputString = (const char *)[formMatrix stringValueAt:7];
  350.     [aValue setStringValue: inputString];
  351.     [dbRecordList setValue:aValue
  352.                 forProperty:[[dbModule entity] propertyNamed:"phone"]
  353.                 at:index];
  354.     
  355.             /* set contract number */
  356.     contractNum = [formMatrix intValueAt:8];
  357.     [aValue setIntValue:contractNum];
  358.     [dbRecordList setValue: aValue
  359.                 forProperty:[[dbModule entity] propertyNamed:"contract"]
  360.                 at:index];
  361.  
  362.     return self;
  363. }
  364.  
  365.  
  366. /* Clear the form cells after each SAVE operation to avoid creating
  367.  * duplicate records.
  368.  */
  369. - clearData
  370. {
  371.     int i;
  372.     
  373.     for (i = 0; i < 9; i++)
  374.         [formMatrix setStringValue:""at:i];
  375.     return self;
  376. }
  377.  
  378. @end
  379.  
  380.